/*    Copyright 2010 Tobias Marschall
 *
 *    This file is part of MoSDi.
 *
 *    MoSDi is free software: you can redistribute it and/or modify
 *    it under the terms of the GNU General Public License as published by
 *    the Free Software Foundation, either version 3 of the License, or
 *    (at your option) any later version.
 *
 *    MoSDi is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *    GNU General Public License for more details.
 *
 *    You should have received a copy of the GNU General Public License
 *    along with MoSDi.  If not, see <http://www.gnu.org/licenses/>.
 */

package mosdi.discovery.objectives;

import mosdi.discovery.ObjectiveFunction;
import mosdi.discovery.ScoreAndPValue;
import mosdi.discovery.MotifFinder.SearchState;
import mosdi.distributions.DistributionConvolver;
import mosdi.distributions.FullCacheDistributionConvolver;
import mosdi.index.SuffixTree;

/** Search for a the motif with optimal p-value w.r.t. to position specific
 *  annotations. */
public class AnnotationSumObjective implements ObjectiveFunction {

	// private SearchState searchState;
	private int[] scoreMaximumAnnotation;
	private int[] scoreSumAnnotation;
	private int[] occurrenceCountAnnotation;
	DistributionConvolver scoreDistribution;
	private int maxOccurrences;
	
	/** Constructor.
	 * 
	 * @param maxOccurrences The maximum number of pattern occurrences. Patterns with more 
	 *                       occurrences are not found.
	 * @param scoreDistribution Distribution of the sum of annotations of a single
	 *                          occurrence.
	 * @param scoreMaximumAnnotation Annotation of suffix tree nodes as produced by
	 *                               ScoreIndexCalculator.              
	 * @param scoreSumAnnotation Annotation of suffix tree nodes as produced by
	 *                           ScoreIndexCalculator.              
	 */
	public AnnotationSumObjective(int maxOccurrences, double[] scoreDistribution, int[] occurrenceCountAnnotation, int[] scoreMaximumAnnotation, int[] scoreSumAnnotation) {
		this.maxOccurrences = maxOccurrences;
		this.occurrenceCountAnnotation = occurrenceCountAnnotation;
		this.scoreMaximumAnnotation = scoreMaximumAnnotation;
		this.scoreSumAnnotation = scoreSumAnnotation;
		this.scoreDistribution = new FullCacheDistributionConvolver(scoreDistribution, maxOccurrences, false, true);
	}

	@Override
	public void initialize(SearchState searchState) {
		// this.searchState = searchState;
	}

	@Override
	public void updatePattern(int newCharacter, int leftmostChangedPosition) { }

	@Override
	public double pValueLowerBound(int prefixLength, int[] nodes) {
		int max = 0;
		int sum = 0;
		for (int node : nodes) {
			// if score sum is out of range, we cannot compute a bound.
			if (scoreSumAnnotation[node]<0) return 0.0;
			sum += scoreSumAnnotation[node];
			if (sum<0) return 0.0;
			max = Math.max(max, scoreMaximumAnnotation[node]);
		}
		if (sum==0) return 1.0;
		int minOccurrences = sum/max + ((sum%max==0)?0:1);
		if (minOccurrences>maxOccurrences) return 0.0;
		double pvalue = scoreDistribution.getPValue(minOccurrences, sum);
//		if (!result) {
//			Log.printf(Log.Level.DEBUG, "Level %d: %d %d %e\n",searchState.getDepth(), minOccurrences, sum, pvalue);
//		}
		return pvalue;
	}

	
	@Override
	public ScoreAndPValue evaluate(int[] nodes, double pValueThreshold) {
		int sum = 0;
		int occurrences = 0;
		for (int node : nodes) {
			if (scoreSumAnnotation[node]<0) throw new IllegalStateException("Trying to evaluate pattern with unknown score sum.");
			sum += scoreSumAnnotation[node];
			occurrences += occurrenceCountAnnotation[node];
		}
		double pvalue;
		if (occurrences>maxOccurrences) {
			return new ScoreAndPValue();
		} else {
			pvalue = scoreDistribution.getPValue(occurrences, sum);
		}
		return new ScoreAndPValue(occurrences,-Math.log(pvalue));
	}

	
	@Override
	public ScoreAndPValue staticEvaluate(int[] pattern,	boolean considerReverse, SuffixTree suffixTree, double pValueThreshold) {
		throw new UnsupportedOperationException("Not yet implemented.");
	}

	@Override
	public String getName() {
		return "annotation-sum";
	}
	
}
